/*
 *  6.7.7 Cancel Asynchronous I/O Operation, P1003.1b-1993, p. 163
 *
 *  COPYRIGHT (c) 1989-2007.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id: aio_cancel.c,v 1.2 2009/01/02 10:04:23 ralf Exp $
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>
#include <aio.h>
#include <aio_misc.h>
#include <errno.h>

#include <rtems/system.h>
#include <rtems/seterr.h>


/*
 *  aio_cancel
 *
 * Cancel an asynchronous I/O request
 *
 *  Input parameters:
 *        fildes - file descriptor
 *        aiocbp - asynchronous I/O control block
 *
 *  Output parameters:
 *        AIO_CANCELED    - if request was canceled
 *        AIO_NOTCANCELED - if at least one of the requested 
 *                          operation(s) cannot be canceled 
 *                          because it is in progress
 */

int
aio_cancel (int filedes, struct aiocb *aiocbp)
{

  rtems_chain_node *node = NULL;
  rtems_chain_node *aux = NULL;
  rtems_chain_control *chain = NULL;
  rtems_aio_request *perprio;
  int result = AIO_ALLDONE;

  if (fcntl (filedes, F_GETFL) < 0)
    rtems_set_errno_and_return_minus_one (EBADF);

  pthread_mutex_lock (&aio_requests_mutex);

  if (aiocbp != NULL) {

    if (aiocbp->aio_fildes != filedes)
      rtems_set_errno_and_return_minus_one (EINVAL);

    else if (aiocbp->error_code == EINPROGRESS) {
      node = rtems_aio_find_fd (filedes);
      if (node == NULL) {
      not_found:
        pthread_mutex_unlock (&aio_requests_mutex);
        rtems_set_errno_and_return_minus_one (EINVAL);
      }

      chain = &((rtems_aio_chain *) node)->perfd;
      aux = chain->first;
      perprio = (rtems_aio_request *) aux;

      while (!rtems_chain_is_tail (chain, aux) && perprio->aiocbp != aiocbp) {
        aux = aux->next;
        perprio = (rtems_aio_request *) aux;
      }

      if (rtems_chain_is_tail (chain, aux))
        goto not_found;

      if (perprio->running == AIO_ALLOCATED)
        result = AIO_NOTCANCELED;
      else {
        rtems_aio_remove_request (node, aux, 0);
        result = AIO_CANCELED;
      }
    }

  } else {
    node = rtems_aio_find_fd (filedes);

    if (node != NULL) {
      chain = &((rtems_aio_chain *) node)->perfd;
      aux = chain->first;
      perprio = (rtems_aio_request *) aux;

      if (perprio->running == AIO_ALLOCATED) {
        aux = aux->next;
        result = AIO_NOTCANCELED;

        if (!rtems_chain_is_tail (chain, aux))
          rtems_aio_remove_request (node, aux, 1);
      } else {
        result = AIO_CANCELED;
        rtems_aio_remove_request (node, aux, 1);

      }
    }
  }

  while (!rtems_chain_is_tail (chain, aux)) {
    node = aux;
    perprio = (rtems_aio_request *) aux;
    assert (perprio->running == AIO_YES || perprio->running == AIO_QUEUED);
    perprio->aiocbp->error_code = ECANCELED;
    perprio->aiocbp->return_value = -1;
    aux = aux->next;
    rtems_aio_free_request ((rtems_aio_request *) node);
  }

  pthread_mutex_unlock (&aio_requests_mutex);

  return result;

}
